package com.example.demo;

import com.example.demo.Client.TransportTemplate;
import com.example.demo.bean.Article;
import com.example.demo.bean.Goods;
import com.example.demo.bean.Type;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Requests;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.net.ContentHandler;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * Elasticsearch的基本测试
 * @ClassName: ElasticsearchTest1
 * @author sunt
 * @date 2017年11月22日
 * @version V1.0
 */
public class ElasticsearchTest2 {

   // private Logger logger = LoggerFactory.getLogger(ElasticsearchTest2.class);

//    public final static String HOST = "192.168.12.156";

    public final static String HOST = "127.0.0.1";

    public final static int PORT = 9300; //http请求的端口是9200，客户端是9300

    private TransportClient client = null;
    /**
     * 获取客户端连接信息
     * @Title: getConnect
     * @author sunt
     * @date 2017年11月23日
     * @return void
     * @throws UnknownHostException
     */
    @SuppressWarnings({ "resource", "unchecked" })
    @Before
    public void getConnect() throws UnknownHostException {
        client = new PreBuiltTransportClient(Settings.EMPTY).addTransportAddresses(
                new InetSocketTransportAddress(InetAddress.getByName(HOST),PORT));
       // logger.info("连接信息:" + client.toString());
        System.out.println("连接信息:" + client.toString());
    }

    /**
     * 关闭连接
     * @Title: closeConnect
     * @author sunt
     * @date 2017年11月23日
     * @return void
     */
    @After
    public void closeConnect() {
        if(null != client) {
            //logger.info("执行关闭连接操作...");
            System.out.println("执行关闭连接操作...");
            client.close();
        }
    }

    /**
     * 创建索引库(添加内容)
     * @Title: addIndex1
     * @author sunt
     * @date 2017年11月23日
     * @return void
     * 索引库的名称必须为小写
     * @throws IOException
     */
    @Test
    public void addIndex() throws IOException {
        System.out.println("测试中");
        /*
         *建立文档对象
         * 参数一blog1：表示索引对象
         * 参数二article：类型
         * 参数三1：建立id
         */
//        long time1 = System.currentTimeMillis();
        for (int i=1;i<2;i++
             ) {
            XContentBuilder builder = XContentFactory.jsonBuilder()
                    .startObject()
                    .field("id", i)
                    .field("title", "烦得很撒粉红色的客户反馈说")
                    .field("content",
                            "服务器")
                    .endObject();
          //  String.valueOf(i)
            IndexResponse indexResponse = client.prepareIndex("blog1", "article",Integer.toString(i)).setSource(builder).get();
        }
//        long time2 = System.currentTimeMillis();
//        System.out.println("插入10万数据花费"+(time2-time1)/1000/60+"分钟");
        // 结果获取
//        String index = indexResponse.getIndex()
//        String type = indexResponse.getType();
//        String id = indexResponse.getId();
//        long version = indexResponse.getVersion();
//        RestStatus status = indexResponse.status();
//        System.out.println("index索引："+index + "--类型：" + type + "--索引id：" + id + "--版本：" + version + "--状态：" + status);
    }

    /**
     * 创建可以ik分词的索引
     * @throws Exception
     */
    @Test
    public void addIndexByIK() throws Exception{
        //创建索引
        client.admin().indices().prepareCreate("blog2").get();
        // 添加映射
        /**
         * 格式：
         * "mappings" : {
         "article" : {
         "dynamic" : "false",
         "properties" : {
         "id" : { "type" : "string" },
         "content" : { "type" : "string" },
         "author" : { "type" : "string" }
         }
         }
         }
         */
        //标准字段映射的拼接
//        XContentBuilder builder = XContentFactory.jsonBuilder()
//                .startObject()
//                .startObject("article")
//                .startObject("properties")
//                .startObject("id")
//                .field("type", "integer").field("store", "yes")
//                .endObject()
//                .startObject("title")
//                .field("type", "string").field("store", "yes").field("analyzer", "ik_max_word")
//                .endObject()
//                .endObject()
//                .endObject()
//                .endObject();
        XContentBuilder builder = XContentFactory.jsonBuilder()
                .startObject()
                .startObject("goods")
                .startObject("properties")
                .startObject("id")
                .field("type", "integer").field("store", "yes")
                .endObject()
                .startObject("name")
                .field("type", "string").field("store", "yes").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("lowerPrice")
                .field("type", "integer").field("store", "yes")
                .endObject()
                .startObject("highPrice")
                .field("type", "integer").field("store", "yes")
                .endObject()
                .startObject("desc")
                .field("type", "string").field("store", "yes").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("content")
                .field("type","object")
                .startObject("properties")
                .startObject("model")
                .field("type", "string").field("store", "yes").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("color")
                .field("type", "string").field("store", "yes").field("analyzer", "ik_max_word")
                .endObject()
                .startObject("size")
                .field("type", "integer").field("store", "yes")
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject();

        // 创建映射
        PutMappingRequest mapping = Requests.putMappingRequest("blog2")
                .type("goods").source(builder);
        client.admin().indices().putMapping(mapping).get();
    }

    /**
     * 获取指定文档信息，通过id查询
     * @throws Exception
     */
    @Test
    public void getIndexNoMapping() throws Exception {
        GetResponse actionGet = client.prepareGet("blog2", "goods", "1").execute().actionGet();
        System.out.println(actionGet.getSourceAsString());
    }

    /**
     * matchAllQuery() 查询所有数据
     * @throws IOException
     */
    @Test
    public void getMatchAll() throws IOException {
//        long time1 = System.currentTimeMillis();
        // get() === execute().actionGet()
        SearchResponse searchResponse = client.prepareSearch("blog2")
                .setTypes("goods").setQuery(QueryBuilders.matchAllQuery())
                .get();
        // 获取命中次数，查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有：" + hits.getTotalHits() + "条");
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            // 每个查询对象
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
            System.out.println("title:" + searchHit.getSource().get("title"));
        }
//        long time2 = System.currentTimeMillis();
//        System.out.println("花费"+(time2-time1)+"毫秒");
    }

    /**
     * queryStringQuery() 关键词查询--好像可以适用所有查询？？？
     * @throws IOException
     */
    @Test
    public void getKeyWord() throws IOException {
        long time1 = System.currentTimeMillis();
        SearchResponse searchResponse = client.prepareSearch("blog2")
                .setTypes("goods").setQuery(QueryBuilders.queryStringQuery("FD"))
                .get();
        // 获取命中次数，查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有：" + hits.getTotalHits() + "条");
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            // 每个查询对象
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
            System.out.println("title:" + searchHit.getSource().get("title"));
        }
        long time2 = System.currentTimeMillis();
        System.out.println("花费"+(time2-time1)+"毫秒");
    }

    /**
     *  wildcardQuery 模糊查询(通配符)--“*”表示0到多个字符，而使用“？”表示一个字符
     *  termQuery 词条查询
     * @throws IOException
     */
    @Test
    public void getByLike() throws IOException {
        long time1 = System.currentTimeMillis();
        SearchResponse searchResponse = TransportTemplate.getClient().prepareSearch("blog2")
                .setTypes("goods").setQuery(QueryBuilders.wildcardQuery("desc","可爱*"))
//                .setTypes("article").setQuery(QueryBuilders.wildcardQuery("content","服务器"))
//                .setTypes("article").setQuery(QueryBuilders.termQuery("content","全文"))
                //一般情况下只显示十条数据
                //from + size must be less than or equal to: [10000]
                //Scroll Search->支持1万以上的数据量
                // .setSize(10000)
                .get();
        // 获取命中次数，查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有：" + hits.getTotalHits() + "条");
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            // 每个查询对象
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
            System.out.println("title:" + searchHit.getSource().get("title"));
        }
        long time2 = System.currentTimeMillis();
        System.out.println("花费"+(time2-time1)+"毫秒");
    }

    /**
     * 组合查询
     * @throws Exception
     */
    @Test
    public void combinationQuery() throws Exception{
        SearchResponse searchResponse = client.prepareSearch("blog1").setTypes("article")
                .setQuery(QueryBuilders.boolQuery().must(QueryBuilders.termQuery("title", "搜索"))// 词条查询
                        //.must(QueryBuilders.rangeQuery("id").from(1).to(5))  // 范围查询
                        //因为IK分词器，在存储的时候将英文都变成了小写
                        .must(QueryBuilders.wildcardQuery("content", "Rest*".toLowerCase())) // 模糊查询
                        .must(QueryBuilders.queryStringQuery( "服电风扇丰盛的分器")) // 关键字（含有）
                )
                .get();
        SearchHits hits = searchResponse.getHits();
        System.out.println("总记录数："+hits.getTotalHits());
        Iterator<SearchHit> iterator = hits.iterator();
        while(iterator.hasNext()){
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString());
            Map<String, Object> source = searchHit.getSource();
//            System.out.println(source.get("id")+"    "+source.get("title")+"    "+source.get("content"));
        }

    }

    /**
     * 布尔查询。条件拼接
     * @throws IOException
     */
    @Test
    public void querySomething() throws IOException {
        long time1 = System.currentTimeMillis();
         String a  ="content.color";
        Goods goods = new Goods();
        goods.setDesc("可爱");
        SearchResponse searchResponse = client.prepareSearch("blog2")
                //全字段只要包含参数必查。
//                .setTypes("goods").setQuery(QueryBuilders.queryStringQuery(""))
                //多个setTypes可以拼接，为交集
                //wildcardQuery三个字以上就不行了。属性中包含这个词组块||字块（"*服务器*"可以得到结果）
//                .setTypes("goods").setQuery(QueryBuilders.wildcardQuery("content.color","服*器"))
//                .setTypes("goods").setQuery(QueryBuilders.wildcardQuery("desc","14"))
                //还没有详细介绍
//                .setTypes("goods").setQuery(QueryBuilders.fuzzyQuery("content.color","e"))
                //范围查询
                .setTypes("goods").setQuery(QueryBuilders.rangeQuery("lowerPrice").gt(80).lt(300))
                //查询
                .addSort("id", SortOrder.ASC)

                //布尔查询
//                .setTypes("goods").setQuery(QueryBuilders.boolQuery()
                        //must表示一定满足。交集
//                        .must(QueryBuilders.termQuery("desc",goods.getDesc()))
                        //should表示并集
//                        .should(QueryBuilders.termQuery(a, goods.getContent()==null?"":StringUtils.isBlank(goods.getContent().getColor())?"":goods.getContent().getColor())))
                .get();

        // 获取命中次数，查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有：" + hits.getTotalHits() + "条");
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            // 每个查询对象
            SearchHit searchHit = iterator.next();
            System.out.println(searchHit.getSourceAsString()); // 获取字符串格式打印
            System.out.println("title:" + searchHit.getSource().get("title"));
        }
        long time2 = System.currentTimeMillis();
        System.out.println("花费"+(time2-time1)+"毫秒");
    }

    /**
     * 插入对象到索引中
     * @throws IOException
     */
    @Test
    public void insertByObject() throws IOException {
//        Article article = new Article();
//        article.setId(11);
//        article.setTitle("搜索工作其实很快乐");
//        article.setContent("我们希望能够简单地使用JSON通过HTTP的索引数据，实时搜索。");
        for(int i= 10;i<15;i++){
            Goods goods = new Goods();
            goods.setId(i);
            goods.setName("小胖"+i);
            goods.setDesc("可爱的"+i+"好玩的有趣的");
            goods.setLowerPrice(100);
            goods.setHighPrice(200);
            Type type = new Type();
            type.setModel("a-"+i);
            type.setColor("什么都会有的全文服务器搜索引擎");
            type.setSize(18);
            goods.setContent(type);
            ObjectMapper mapper  = new ObjectMapper();

            client.prepareIndex("blog4", "good",null).setSource(mapper.writeValueAsString(goods)).get();
        }


    }

    /**
     * 更新索引中文档，通过对象(没有的属性会变成null或默认值，好吗？？？？？？？？？？)
     * @throws Exception
     */
    @Test
    public void updateByObject() throws Exception {
//        Article article = new Article();
//        article.setId(11);
//        article.setTitle("搜索工作其实很快乐11111");
//        article.setContent("我们希望能够简单地使用JSON通过HTTP的索引数据，实时搜索。");
        Goods article = new Goods();
        article.setId(1);
        article.setName("小胖1");
        article.setDesc("可爱的好玩的有趣的");
        article.setLowerPrice(100);
        article.setHighPrice(150);
        article.setMuch(88);

        Type type = new Type();
        type.setColor("elasticSearch是一个非常好用的服务器搜索引擎");
        type.setSize(18);
        type.setModel("a-1");
        article.setContent(type);
        ObjectMapper mapper  = new ObjectMapper();

        //两种方式
//      client.prepareUpdate("blog1", "article", Integer.toString(article.getId())).setDoc(mapper.writeValueAsString(article)).get();
        client.update(new UpdateRequest("blog2", "goods", Integer.toString(article.getId())).doc(mapper.writeValueAsString(article))).get();

    }

    /**
     * 删除索引中文档（通过文档id---还可以通过别的方式吗？？？）
     * @throws Exception
     */
    @Test
    public void deleteByObject() throws Exception {
        Article article = new Article();
        article.setId(10);
        //两种方式
//        client.prepareDelete("blog1", "article", Integer.toString(article.getId())).get();
        client.delete(new DeleteRequest("blog1", "article", Integer.toString(article.getId()))).get();

    }



    /**
     * 高亮显示（<em></em>）
     * @throws Exception
     */
    @Test
    public void showHighlight() throws Exception{
        ObjectMapper objectMapper = new ObjectMapper();
        // 搜索数据
        SearchRequestBuilder searchRequestBuilder = client
                .prepareSearch("blog1").setTypes("article")
                //.setQuery(QueryBuilders.termQuery("content", "搜索"));
                .setQuery(QueryBuilders.multiMatchQuery("搜索", "title","content"));
        //.setQuery(QueryBuilders.queryStringQuery("搜索").field("title").field("content"))
       // 1、 配置高亮出现的位置和前后缀
        // 高亮定义
        HighlightBuilder hiBuilder=new HighlightBuilder();
        hiBuilder.preTags("<em>");
        hiBuilder.postTags("</em>");
        hiBuilder.field("title").field("content");

        // 设置摘要大小（高亮出现最多的区域）
        //searchRequestBuilder.setHighlighterFragmentSize(100);
        SearchResponse searchResponse = searchRequestBuilder.highlighter(hiBuilder).get();
        // 获取命中次数，查询结果有多少对象
        SearchHits hits = searchResponse.getHits();
        System.out.println("查询结果有：" + hits.getTotalHits() + "条");
        Iterator<SearchHit> iterator = hits.iterator();
        while (iterator.hasNext()) {
            // 每个查询对象
            SearchHit searchHit = iterator.next();

           // 2、 对结果的高亮片段做拼接处理，替换原有内容
            System.out.println(searchHit.getSourceAsString());
            // 将高亮的结果转换成Article对象
            // 将高亮处理后内容，替换原有内容 （原有内容，可能会出现显示不全 ）
            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            // content字段
            HighlightField contentField = highlightFields.get("content");

            // 获取到原有内容中 每个高亮显示 集中位置 fragment 就是高亮片段
            Text[] fragments = contentField.fragments();
            String content = "";
            for (Text text : fragments) {
                content += text;
            }
            // title字段
            HighlightField titleField = highlightFields.get("title");

            // 获取到原有内容中 每个高亮显示 集中位置 fragment 就是高亮片段
            Text[] fragments1 = titleField.fragments();
            String title = "";
            for (Text text : fragments1) {
                title += text;
            }
            // 将查询结果转换为对象
            Article article = objectMapper.readValue(
                    searchHit.getSourceAsString(), Article.class);

            // 用高亮后内容，替换原有内容
            //这时候可以考虑是否更新索引库中文档信息
            if(content!=null && !content.equals("")){
                article.setContent(content);
            }
            if(title!=null && !title.equals("")){
                article.setTitle(title);
            }
//            System.out.println(article);
        }

    }


    /**
     * json和string之间的转换
     * @throws Exception
     */
    @Test
    public void jsonAndString() throws Exception{

        Article article = new Article();
        article.setId(11);
        article.setTitle("杂志");
        article.setContent("青少年的成长问题是祖国进步的关键");
        ObjectMapper mapper = new ObjectMapper();
        String value = mapper.writeValueAsString(article);
        System.out.println(value);

    }
    }

